//
//  FRQBluetoothManager.m
//  FRQBluetoothKit
//
//  Created by chunhai xu on 2021/1/3.
//

#import "FRQBluetoothManager.h"
#import "FRIBluetooth.h"
#import "FRIUpdateOTAManager.h"
#import <CoreBluetooth/CBCentralManager.h>
#import "BlueCommonData.h"

#define FRQCheckStateError do { \
    if ([[FRQBluetoothManager shareManager] _checkStateError]) { \
        return; \
    }\
} while (0);


//特定的服务标识
#define kFRQServiceUUID @"FE00"
#define kFRQWriteCharacteristicUUID @"FF01"
#define kFRQReadCharacteristicUUID @"FF02"
#define CServiceUUID @"FFF0"

@interface FRQBluetoothManager ()<FRIUpdateOTAManagerDelegate>

@property(nonatomic, strong) FRIUpdateOTAManager *updateOTAMgr; //!< 升级OTA
@property(nonatomic, strong) FRIBluetooth *friBLE; //!< 蓝牙工具
@property(nonatomic,strong) CBCharacteristic *writC;
@property(nonatomic,strong) CBCharacteristic *writFRQ;
@property(nonatomic,strong) CBCharacteristic *readFRQ;
@property(nonatomic,strong) CBPeripheral *peripheral;
@property(nonatomic, assign) BOOL needUpdateOTA;
@property (nonatomic,strong) NSString *binFilePath;

@end


@implementation FRQBluetoothManager
@synthesize state;

+(instancetype)shareManager{
    static FRQBluetoothManager *instance = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[FRQBluetoothManager alloc] init];
    });
    return instance;
}


- (instancetype)init
{
    if (self = [super init]) {
        
        //蓝牙外设处理类
        self.friBLE = [FRIBluetooth shareBluetooth];
        
        //        //扫描选项->CBCentralManagerScanOptionAllowDuplicatesKey:忽略同一个Peripheral端的多个发现事件被聚合成一个发现事件
        NSDictionary *scanForPeripheralsWithOptions = @{CBCentralManagerScanOptionAllowDuplicatesKey:@NO};
        //        /*连接选项->
        //         CBConnectPeripheralOptionNotifyOnConnectionKey :当应用挂起时，如果有一个连接成功时，如果我们想要系统为指定的peripheral显示一个提示时，就使用这个key值。
        //         CBConnectPeripheralOptionNotifyOnDisconnectionKey :当应用挂起时，如果连接断开时，如果我们想要系统为指定的peripheral显示一个断开连接的提示时，就使用这个key值。
        //         CBConnectPeripheralOptionNotifyOnNotificationKey:
        //         当应用挂起时，使用该key值表示只要接收到给定peripheral端的通知就显示一个提示
        //        */
        NSDictionary *connectOptions = @{CBConnectPeripheralOptionNotifyOnConnectionKey:@NO,
                                         CBConnectPeripheralOptionNotifyOnDisconnectionKey:@NO};
        [self.friBLE setFriOptionsWithScanForPeripheralsWithOptions:scanForPeripheralsWithOptions connectPeripheralWithOptions:connectOptions scanForPeripheralsWithServices:nil discoverWithServices:nil discoverWithCharacteristics:nil];
        
        [self _setupBLECallbacks];
        
    }
    return self;
}


-(void)_setupBLECallbacks{
    
    __weak typeof(self) weakSelf = self;
    [self.friBLE setBlockOnDiscoverToPeripherals:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        FRILog(@"on discover periheral %@, %@ !!",peripheral.name,advertisementData);
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([strongSelf.delegate respondsToSelector:@selector(onBLEManagerDiscoverPeripheral:peripheral:advertisement:RSSI:)]) {
                [strongSelf.delegate onBLEManagerDiscoverPeripheral:self peripheral:peripheral advertisement:advertisementData RSSI:RSSI];
            }
        });
        
    }];
    
    //蓝牙修改处
    [self.friBLE setBlockOnConnected:^(CBCentralManager *central, CBPeripheral *peripheral) {
        
        __strong typeof(weakSelf) strongSelf = weakSelf;
        strongSelf.peripheral = peripheral;

        [strongSelf cancelScan];
        if (!strongSelf) {
            return;
        }
        
        //本地先存储
        
        FRILog(@"===peripheral<%@> connected !!",peripheral.name);
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([strongSelf.delegate respondsToSelector:@selector(onBLEManagerConnect:peripheral:error:)]) {
                [strongSelf.delegate onBLEManagerConnect:strongSelf peripheral:peripheral error:nil];
            }
        });
    }];
    //蓝牙修改处
    [self.friBLE setBlockOnDiscoverCharacteristics:^(CBPeripheral *peripheral, CBService *service, NSError *error) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        FRILog(@"on discover characteristic for service %@!!",service.UUID.UUIDString);
        if ([service.UUID.UUIDString.uppercaseString containsString:kFRQServiceUUID]) {
            
            [strongSelf _checkAvaliableCharacters:peripheral service:service];
        }
        if ([service.UUID.UUIDString.uppercaseString containsString:CServiceUUID]){
            for (CBCharacteristic *character in service.characteristics) {
                if ([character.UUID.UUIDString isEqualToString:@"FFF3"]){
                    //                    [SkToast SkToastShow:@"发现写的特征"];
                    strongSelf.writC = character;
                }
                if ([character.UUID.UUIDString isEqualToString:@"0783B03E-8535-B5A0-7140-A304D2495CB8"]) {
                    [strongSelf.friBLE notify:peripheral characteristic:character block:^(CBPeripheral *peripheral, CBCharacteristic *characteristics, NSError *error) {
                        if(!error){
                            NSMutableData *data = [NSMutableData dataWithData:characteristics.value];
                            
                            Byte *valueByte = (Byte *)[data bytes];
                            NSLog(@"收到的数据- %@     %ld      %@",data,data.length,[strongSelf hexString:data]);
                            if ([characteristics.UUID.UUIDString isEqualToString:@"0783B03E-8535-B5A0-7140-A304D2495CB8"]){
                                NSMutableData *data = [NSMutableData dataWithData:characteristics.value];
                                
                                Byte *valueByte = (Byte *)[data bytes];
                                NSLog(@"收到的数据- %@     %ld      %@",data,data.length,[strongSelf hexString:data]);
                                for (int i = 0; i < data.length - 1; i++) {
                                    NSLog(@"转换%x",valueByte[i]);
                                    //文档1.3：设备的响应更新及请求文件数据0x03
                                    if(i == 2 && valueByte[2] == 3){
                                        NSData *decryptData = [data subdataWithRange:NSMakeRange(3, 4)];
                                        NSString *str = [strongSelf hexString:decryptData];
                                        NSLog(@"-------%@",str);
                                        [strongSelf writeOneAndTwoDevice:[strongSelf characterTo16:str]];
                                        break;
                                    }
                                    
                                    //文档2.2：回应校验
                                    if(i == 2 && valueByte[2] == 4){
                                        
                                        [strongSelf writeTwoAndOneDevice];
                                        break;
                                    }
                                    //蓝牙11.30  获取版本号
                                    if(i == 2 && valueByte[2] == 9){
                                        
                                        NSData *decryptData = [data subdataWithRange:NSMakeRange(3, 4)];
                                        NSString *str = [strongSelf hexString:decryptData];
                                        NSLog(@"-------%@",str);
                                        NSInteger version = [strongSelf characterTo16:str];
                                        [strongSelf.imgDelegate onVersionNum:version];
                                        break;
                                    }
                                }
                            }
                            
                        }
                    }];
                }
            }
        }
        
    }];
    
    [self.friBLE setBlockOnDiscoverDescriptorsForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSError *error) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        if (error) {
            FRILog(@"💥💥💥💥on discover descriptors for characteristic %@, Error %@!!",characteristic.UUID.UUIDString,error);
        }
        else{
            FRILog(@"on discover descriptors for characteristic %@!!",characteristic.UUID.UUIDString);
        }
    }];
    
    [self.friBLE setBlockOnFailToConnect:^(CBCentralManager *central, CBPeripheral *peripheral, NSError *error) {
        
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }

        //clean up
        [strongSelf cleanupForPeripheral:peripheral];
        
        FRILog(@"===fail to connect peripheral<%@> !! , error %@",peripheral.name,error);
        NSError *convertError = nil;
        if (error) {
            convertError = [FRQBluetoothManager convertErrFrom:error];
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([strongSelf.delegate respondsToSelector:@selector(onBLEManagerConnect:peripheral:error:)]) {
                [strongSelf.delegate onBLEManagerConnect:strongSelf peripheral:peripheral error:convertError];
            }
        });
        
    }];
    
    [self.friBLE setBlockOnDiscoverServices:^(CBPeripheral *peripheral, NSError *error) {
        FRILog(@"===discover <%@>  services %@ , error %@ !!",peripheral.name,peripheral.services,error);
    }];
    
    
    [self.friBLE setBlockOnDisconnect:^(CBCentralManager *central, CBPeripheral *peripheral, NSError *error) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
//        [self ]
        if (!strongSelf) {
            return;
        }
        FRILog(@"===peripheral<%@> disconnected , error %@ !!",peripheral.name,error);
        NSError *convertError = nil;
        if (error) {
            convertError = [FRQBluetoothManager convertErrFrom:error];
            [strongSelf cleanupForPeripheral:peripheral];
        }
        
        if (strongSelf.updateOTAMgr.otaStatus == FRIOTAStatusFinish && self.needUpdateOTA) { //如果状态完成那么忽略错误提示
            return;
        }
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([strongSelf.delegate respondsToSelector:@selector(onBLEManagerDisconnect:peripheral:error:)]) {
                [strongSelf.delegate onBLEManagerDisconnect:strongSelf peripheral:peripheral error:convertError];
            }
        });
        
    }];
    
    [self.friBLE setBlockOnCentralManagerDidUpdateState:^(CBCentralManager *centralManager) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        
        strongSelf.state = centralManager.state;
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([strongSelf.delegate respondsToSelector:@selector(onBLEManagerStateChange:)]) {
                [strongSelf.delegate onBLEManagerStateChange:strongSelf];
            }
            
            
            //如果蓝牙设备已经打开，那么返回
            if (strongSelf.state == CBCentralManagerStatePoweredOn && [strongSelf.friBLE allRestorePeripherals].count > 0) {
                
                for (CBPeripheral *restorePeripheral in [strongSelf.friBLE allRestorePeripherals]) {
                    [strongSelf cleanupForPeripheral:restorePeripheral];
                }
                
                //                if ([strongSelf.delegate respondsToSelector:@selector(onBLEManager:willRestorePeripherals:)]) {
                //                    [strongSelf.delegate onBLEManager:strongSelf willRestorePeripherals:strongSelf.friBLE.allRestorePeripherals];
                //                }
            }
        });
        
    }];
    
    [self.friBLE setBlockOnCancelScanBlock:^(CBCentralManager *centralManager) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([strongSelf.delegate respondsToSelector:@selector(onBLEManagerCancelScan:)]) {
                [strongSelf.delegate onBLEManagerCancelScan:strongSelf];
            }
        });
    }];
    
    [self onReadValueForCharacteristicAtChannel];
    
}
//蓝牙修改处
- (void)onReadValueForCharacteristicAtChannel{
    __weak typeof(self) weakSelf = self;
    [self.friBLE setBlockOnReadValueForCharacteristic:^(CBPeripheral *peripheral, CBCharacteristic *characteristic, NSError *error) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
    }];
}
//蓝牙 11.30
#pragma mark - - 1.1 APP端发起更新(获取文件大小)
-(void)writeOneAndOneDevice:(NSString *)path{
    //如果为网络文件
//    NSData *data = self.fileData;
    self.binFilePath=path;
    NSData * fileData = [self getFileData];
    NSData *data = [BlueCommonData createFirmwareUpdatePacketWithFileSize:fileData.length];
    [self.imgDelegate onImageStartAction];
    [self writeBluetooth:data];
}
//蓝牙 11.30
#pragma mark - - 1.2 APP端回复数据指令(即继续传输文件data,每次最多只传384)
-(void)writeOneAndTwoDevice:(NSInteger)location{
    //如果为网络文件
//    NSData *data = self.fileData;
    NSData * fileData = [self getFileData];
    if(fileData.length == location){
        NSLog(@"上传图片成功");
        [self.imgDelegate onImageFinishAction:location];
    }else{
        [self.imgDelegate onImageDidSendAction:location andLength:fileData.length];
        __weak typeof(self) weakSelf = self;
        [BlueCommonData sendDataPacket:fileData andLocation:location andBlock:^(NSData * _Nonnull blueData) {
            __strong typeof(weakSelf) strongSelf = weakSelf;
            [strongSelf writeBluetooth:blueData];
        }];
    }
}
#pragma mark - - 2.1 发起校验
-(void)writeTwoAndOneDevice{
    NSData *data = [BlueCommonData buildCommandPacketWithVersion:0x5AA5];
    [self writeBluetooth:data];
}
#pragma mark - - 3.3 发起校验
-(void)writeThreeAndThreeDevice{
    NSData *data = [BlueCommonData buildCommandPacketWithVersionNum];
    [self writeBluetooth:data];
}
#pragma mark - - 写入蓝牙(写消息给服务端)
- (void)writeBluetooth:(NSData *)data{
    if(!self.writC){
        NSLog(@"请重新连接蓝牙");
        return;
    }
    if (self.writC.properties & CBCharacteristicPropertyWriteWithoutResponse) {
        [self.peripheral writeValue:data forCharacteristic:self.writC type:CBCharacteristicWriteWithoutResponse];
        NSLog(@"写入蓝牙数据1");
    }else{
        NSLog(@"写入蓝牙数据2");
        [self.peripheral writeValue:data forCharacteristic:self.writC type:CBCharacteristicWriteWithResponse];
    }
}
#pragma mark - - 获取文件data
- (NSData *)getFileData{
//    NSURL *fileUrl = [[NSBundle mainBundle] URLForResource:@"g4.bin" withExtension:nil];
//    NSData * fileData = [NSData dataWithContentsOfURL:fileUrl];
    NSString *fileUrl=self.binFilePath;
    NSData *fileData = [NSData dataWithContentsOfFile:fileUrl];
    return fileData;
}
-(void)_checkAvaliableCharacters:(CBPeripheral *)peripheral service:(CBService *)service{
    
    CBCharacteristic *writeCharacteristic = nil;
    CBCharacteristic *readCharacteristic = nil;
    for (CBCharacteristic *character in service.characteristics) {
        
        if ([character.UUID.UUIDString.uppercaseString containsString:kFRQWriteCharacteristicUUID]
            && ((character.properties&CBCharacteristicPropertyWrite) || (character.properties&CBCharacteristicPropertyWriteWithoutResponse))) { //写
            writeCharacteristic = character;
            self.writFRQ = character;
            FRILog(@"⚽️⚽️⚽️⚽️⚽️⚽️get writable charact %@, %d",character, ((character.properties&CBCharacteristicPropertyWrite) || (character.properties&CBCharacteristicPropertyWriteWithoutResponse)));
        }
        else if ([character.UUID.UUIDString.uppercaseString containsString:kFRQReadCharacteristicUUID]
                 && (character.properties&CBCharacteristicPropertyRead)){ //读
            readCharacteristic = character;
            self.readFRQ = character;
            FRILog(@"⚽️⚽️⚽️⚽️⚽️⚽️get readable charact %@, %lu",character,character.properties&CBCharacteristicPropertyRead);
        }
    }
    
    if (self.needUpdateOTA) {
        //开始升级设备
        if (writeCharacteristic && readCharacteristic) {
            [self.updateOTAMgr startUpdateOTA:peripheral writeCharacteristic:writeCharacteristic readCharacteristic:readCharacteristic];
        }
        else{
            
            dispatch_async(dispatch_get_main_queue(), ^{
                if ([self.delegate respondsToSelector:@selector(onBLEManagerUpdateOTAFinish:error:)]) {
                    NSError *error = [NSError errorWithDomain:@"The device cannot read or write, please try again ~" code:FRQErrorCode_ota userInfo:@{NSLocalizedFailureReasonErrorKey:@"Device cannot read or write, please try again~",NSLocalizedDescriptionKey:@"The device cannot read or write, please try again ~"}];
                    [self.delegate onBLEManagerUpdateOTAFinish:self error:error];
                }
            });
            
        }
    }
    
}


#pragma mark -- public
- (void)setFilterForDiscoverPeripherals:(BOOL (^)(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI))discoverFilterBlock andFilterForConnectToPeripherals:(BOOL (^)(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI))connectFilterBlock{
    
    [self.friBLE setFilterOnDiscoverPeripherals:^BOOL(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI) {
        if (discoverFilterBlock) {
            return discoverFilterBlock(peripheralName, advertisementData, RSSI);
        }
        return YES;
    }];
    
    [self.friBLE setFilterOnConnectToPeripherals:^BOOL(NSString *peripheralName, NSDictionary *advertisementData, NSNumber *RSSI) {
        if (connectFilterBlock) {
            return connectFilterBlock(peripheralName, advertisementData, RSSI);
        }
        return YES;
    }];
    
}

- (void)scanPeripherals{
    
    FRQCheckStateError
    
    //扫描设备
    self.friBLE.scanForPeripherals().begin();
    
}

- (void)connectToPeripheral:(CBPeripheral *)peripheral{
    
    FRQCheckStateError
    
    //只要是状态未连接那么开始连接
    self.needUpdateOTA = NO;
    
    //开始连接
    self.friBLE.having(peripheral).enjoy();
    
}

- (void)autoReconnectToPeripheral:(CBPeripheral *)peripheral{
    
    FRQCheckStateError
    
    [self connectToPeripheral:peripheral];
    //添加自动重连设备
    [self.friBLE AutoReconnect:peripheral];
    
}

- (void)closeAuotReconnectPeripheral:(CBPeripheral *)peripheral{
    
    [self closePeripheralConnection:peripheral];
    [self.friBLE AutoReconnectCancel:peripheral];
}

- (void)closePeripheralConnection:(CBPeripheral *)peripheral{
    [self.friBLE cancelPeripheralConnection:peripheral];
}


- (void)closeAllPeripheralsConnection{
    [self.friBLE cancelAllPeripheralsConnection];
}

- (NSArray *)allConnectedPeripherals{
    return [self.friBLE findConnectedPeripherals];
}

- (CBPeripheral *)findConnectedPeripheralWithName:(NSString *)peripheralName{
    return [self.friBLE findConnectedPeripheral:peripheralName];
}

- (BOOL)isScanning{
    return self.friBLE.centralManager.isScanning;
}

- (CBCentralManager *)centralManager{
    return self.friBLE.centralManager;
}

- (void)cancelScan{
    
    if (!self.isScanning) {
        return;
    }
    
    [self.friBLE cancelScan];
    
}


- (void)closeAllPeripheralsConnection:(nonnull void (^)(id<FRBleAbility> _Nonnull))block {
    
    __weak typeof(self) weakSelf = self;
    [self.friBLE setBlockOnCancelAllPeripheralsConnectionBlock:^(CBCentralManager *centralManager) {
        __strong typeof(weakSelf) strongSelf = weakSelf;
        if (!strongSelf) {
            return;
        }
        
        FRILog(@"cancel all peripherals connection !!!");
        
        if (block) {
            block(strongSelf);
        }
    }];
    
    [self.friBLE cancelAllPeripheralsConnection];
    
}

///OTA
- (void)updateOTAWithFilePath:(NSString *)filePath  toPeripheral:(CBPeripheral*)perpheral{
    
    FRQCheckStateError
    
    if (filePath.length <= 0 || ![filePath.pathExtension isEqualToString:@"bin"]) {
        
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([self.delegate respondsToSelector:@selector(onBLEManagerUpdateOTAFinish:error:)]) {
                NSError *error = [NSError errorWithDomain:@"The file path is incorrect. Please try again" code:FRQErrorCode_ota userInfo:@{NSLocalizedFailureReasonErrorKey:@"The file path is incorrect. Please try again",NSLocalizedDescriptionKey:@"The file path is incorrect. Please try again"}];
                [self.delegate onBLEManagerUpdateOTAFinish:self error:error];
            }
        });
        
        return;
    }
    else if(![[NSFileManager defaultManager] fileExistsAtPath:filePath]){
        dispatch_async(dispatch_get_main_queue(), ^{
            if ([self.delegate respondsToSelector:@selector(onBLEManagerUpdateOTAFinish:error:)]) {
                NSError *error = [NSError errorWithDomain:@"The file does not exist. Please try again" code:FRQErrorCode_ota userInfo:@{NSLocalizedFailureReasonErrorKey:@"The file does not exist. Please try again",NSLocalizedDescriptionKey:@"The file path is incorrect. Please try again"}];
                [self.delegate onBLEManagerUpdateOTAFinish:self error:error];
            }
        });
        
        return;
    }
    
    NSData *otaData = [NSData dataWithContentsOfFile:filePath];
    [self updateOTAWithData:otaData toPeripheral:perpheral];
    
}

- (void)updateOTAWithData:(NSData *)binData  toPeripheral:(CBPeripheral*)perpheral{
    
    FRQCheckStateError
    
    //OTA升级管理
    self.updateOTAMgr.friBLE = self.friBLE;
    self.updateOTAMgr.binData = binData;
    [self.updateOTAMgr resetOTAStatus]; //状态重置
    
    self.needUpdateOTA = YES;
    
    //进行设备连接
    self.friBLE.having(self.peripheral).connectToPeripherals().discoverServices().discoverCharacteristics().discoverDescriptorsForCharacteristic().begin();
    
}
- (void)cancelOTAUpgrade{
    
    [_updateOTAMgr cancelOTAUpdate];
}


- (FRIUpdateOTAManager *)updateOTAMgr{
    if (!_updateOTAMgr) {
        _updateOTAMgr = [[FRIUpdateOTAManager alloc] init];
        _updateOTAMgr.delegate = self;
    }
    return _updateOTAMgr;
}



#pragma mark -- FRIUpdateOTAManagerDelegate
-(void)onOTAUpdateStart:(FRIUpdateOTAManager *)ota{
    
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.delegate respondsToSelector:@selector(onBLEManagerBeginUpdateOTA:)]) {
            [self.delegate onBLEManagerBeginUpdateOTA:self];
        }
    });
}

-(void)onOTAUpdateStatusDidChange:(FRIUpdateOTAManager *)ota withProgress:(float)aProgress{
    
    FRILog(@"========  OTA progress %.f =========",aProgress);
    dispatch_async(dispatch_get_main_queue(), ^{
        
        if ([self.delegate respondsToSelector:@selector(onBLEManagerUpdateOTA:progress:)]) {
            [self.delegate onBLEManagerUpdateOTA:self progress:aProgress];
        }
    });
    
}

-(void)onOTAUpdateStatusCompletion:(FRIUpdateOTAManager *)ota{
    dispatch_async(dispatch_get_main_queue(), ^{
        
        if ([self.delegate respondsToSelector:@selector(onBLEManagerUpdateOTAFinish:error:)]) {
            [self.delegate onBLEManagerUpdateOTAFinish:self error:nil];
        }
    });
    
}
-(void)onOTAUpdateStatusFailure:(FRIUpdateOTAManager *)ota error:(NSError *)err{
    
    dispatch_async(dispatch_get_main_queue(), ^{
        if ([self.delegate respondsToSelector:@selector(onBLEManagerUpdateOTAFinish:error:)]) {
            [self.delegate onBLEManagerUpdateOTAFinish:self error:err];
        }
    });
    
}

#pragma mark -- doCheckCentralState
-(BOOL)_checkStateError{
    
    
    if (self.state == FRQManagerStatePoweredOn || self.state == FRQManagerStateUnknown) {
        return NO;
    }
    
    NSString *stateErrMsg = @"Device connection failed, please try again ~";
    if (self.state == FRQManagerStateUnsupported) {
        stateErrMsg = @"Bluetooth devices are not supported";
    }
    else if (self.state == FRQManagerStateUnauthorized){
        stateErrMsg = @"The Bluetooth device is not authorized, please enable it in the Settings and try again";
    }
    else if (self.state == FRQManagerStatePoweredOff){
        stateErrMsg = @"The Bluetooth device is turned off. Turn it on and try again";
    }
    dispatch_async(dispatch_get_main_queue(), ^{
        
        NSString *errMsg = [NSString stringWithFormat:@"<%@>%@",@(self.state),stateErrMsg];
        if ([self.delegate respondsToSelector:@selector(onBLEManagerUpdateOTAFinish:error:)]) {
            NSError *error = [NSError errorWithDomain:errMsg code:FRQErrorCode_connect userInfo:@{NSLocalizedFailureReasonErrorKey:errMsg,NSLocalizedDescriptionKey:errMsg}];
            [self.delegate onBLEManagerUpdateOTAFinish:self error:error];
        }
    });
    
    return YES;
}


+(NSError *)convertErrFrom:(NSError *)err{
    
    
    //    CBErrorInvalidParameters NS_ENUM_AVAILABLE(10_9, 6_0)        = 1,
    //    CBErrorInvalidHandle NS_ENUM_AVAILABLE(10_9, 6_0)            = 2,
    //    CBErrorNotConnected NS_ENUM_AVAILABLE(10_9, 6_0)            = 3,
    //    CBErrorOutOfSpace NS_ENUM_AVAILABLE(10_9, 6_0)                = 4,
    //    CBErrorOperationCancelled NS_ENUM_AVAILABLE(10_9, 6_0)        = 5,
    //    CBErrorConnectionTimeout NS_ENUM_AVAILABLE(10_9, 6_0)        = 6,
    //    CBErrorPeripheralDisconnected NS_ENUM_AVAILABLE(10_9, 6_0)    = 7,
    //    CBErrorUUIDNotAllowed NS_ENUM_AVAILABLE(10_9, 6_0)            = 8,
    //    CBErrorAlreadyAdvertising NS_ENUM_AVAILABLE(10_9, 6_0)        = 9,
    //    CBErrorConnectionFailed NS_ENUM_AVAILABLE(10_13, 7_1)        = 10,
    //    CBErrorConnectionLimitReached NS_ENUM_AVAILABLE(10_13, 9_0)    = 11,
    //    CBErrorUnkownDevice NS_ENUM_DEPRECATED(10_13, 10_15, 9_0, 13_0, "Use CBErrorUnknownDevice instead") = 12,
    //    CBErrorUnknownDevice NS_ENUM_AVAILABLE(10_14, 12_0)            = 12,
    //    CBErrorOperationNotSupported NS_ENUM_AVAILABLE(10_14, 12_0)    = 13,
    //    CBErrorPeerRemovedPairingInformation NS_ENUM_AVAILABLE(10_15, 13_4)    = 14,
    //    CBErrorEncryptionTimedOut NS_ENUM_AVAILABLE(10_15, 13_3)    = 15,
    //    CBErrorTooManyLEPairedDevices NS_ENUM_AVAILABLE(11_0, 14_0) = 16,
    
    NSString *msg = @"Unknown error, please try again";
    switch (err.code) {
        case CBErrorConnectionTimeout:
            msg = @"Device connection timeout";
            break;
        case CBErrorOperationCancelled:
            msg = @"Operation cancelled";
            break;
        case CBErrorAlreadyAdvertising:
            msg = @"Device subscribed";
            break;
        case CBErrorOperationNotSupported:
            msg = @"Operation not supported";
            break;
        case CBErrorConnectionLimitReached:
            msg = @"The number of device connections reached the upper limit. Procedure";
            break;
        case CBErrorEncryptionTimedOut:
            msg = @"Encryption timeout";
            break;
        case CBErrorConnectionFailed:
            msg = @"Connection failure";
            break;
        case CBErrorPeerRemovedPairingInformation:
            msg = @"The device pairing information has been deleted";
            break;
        default:
            break;
    }
    NSString *localMessage = [NSString stringWithFormat:@"<%@>%@",@(err.code),msg];
    return [NSError errorWithDomain:localMessage code:FRQErrorCode_connect userInfo:@{NSLocalizedDescriptionKey:localMessage,
                                                                                      NSLocalizedFailureReasonErrorKey:localMessage}];
}


//This is called when things either go wrong, or we are done with the connection. This cancels any subscriptions if there are any, or straight disconnects if not.
- (void)cleanupForPeripheral:(CBPeripheral *)peripheral
{
    //See if we are subscribed to a characteristic on the peripheral
    if(peripheral.services.count > 0){
        //Loop through all service
        [peripheral.services enumerateObjectsUsingBlock:^(CBService *service, NSUInteger idx, BOOL *stop) {
            //Loop through all the services characteristics
            [service.characteristics enumerateObjectsUsingBlock:^(CBCharacteristic *characteristic, NSUInteger idx, BOOL *stop) {
                
                //And is the one we are listening to
                if([characteristic isNotifying])
                {
                    //Unsubscribe
                    [peripheral setNotifyValue:NO forCharacteristic:characteristic];
                }
            }];
        }];
    }
    
    //If it jumps to here, we're connected, but we're not subscribed, so we just disconnect
    [self.friBLE cancelPeripheralConnection:peripheral];
    
}
#pragma mark - - 转16进制转10进制
-(NSInteger)characterTo16:(NSString *)hexString{
    
    NSScanner *scanner = [NSScanner scannerWithString:hexString];
    unsigned int decimalValue;
    [scanner scanHexInt:&decimalValue];
    return decimalValue;
}
#pragma mark - - data转16进制字符串
- (NSString *)hexString:(NSData *)data {
    NSUInteger length = data.length;
    NSMutableString *result = [NSMutableString stringWithCapacity:length * 2];
    const unsigned char *byte = data.bytes;
    for (int i = 0; i < length; i++, byte++) {
        [result appendFormat:@"%02X", *byte];
    }
    return result;
}
#pragma mark - - data转16进制字符串
- (void)startConnect:(CBPeripheral*)perpheral{
    //进行设备连接
    self.needUpdateOTA = NO;
    self.friBLE.having(perpheral).connectToPeripherals().discoverServices().discoverCharacteristics().discoverDescriptorsForCharacteristic().begin();
}
- (void)startC{
    //进行设备连接
    self.needUpdateOTA = NO;
    self.friBLE.having(self.peripheral).connectToPeripherals().discoverServices().discoverCharacteristics().discoverDescriptorsForCharacteristic().begin();
}
- (void)disConnectNoti{
    
}
@end
